// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/shared_memory_data_consumer_handle.h"

#include <stddef.h>
#include <string.h>

#include <string>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_split.h"
#include "base/task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/child/fixed_received_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {
    using blink::WebDataConsumerHandle;
    using Result = WebDataConsumerHandle::Result;
    using Writer = SharedMemoryDataConsumerHandle::Writer;
    using BackpressureMode = SharedMemoryDataConsumerHandle::BackpressureMode;
    const BackpressureMode kApplyBackpressure = SharedMemoryDataConsumerHandle::kApplyBackpressure;
    const BackpressureMode kDoNotApplyBackpressure = SharedMemoryDataConsumerHandle::kDoNotApplyBackpressure;

    const WebDataConsumerHandle::Flags kNone = WebDataConsumerHandle::FlagNone;
    const Result kOk = WebDataConsumerHandle::Ok;
    const Result kDone = WebDataConsumerHandle::Done;
    const Result kShouldWait = WebDataConsumerHandle::ShouldWait;
    const Result kUnexpectedError = WebDataConsumerHandle::UnexpectedError;

    using ::testing::_;
    using ::testing::InSequence;
    using ::testing::Invoke;
    using ::testing::MockFunction;
    using ::testing::Return;
    using ::testing::StrictMock;

    using Checkpoint = StrictMock<MockFunction<void(int)>>;
    using ReceivedData = RequestPeer::ReceivedData;

    class Logger final : public base::RefCounted<Logger> {
    public:
        Logger() { }
        void Add(const std::string& entry) { log_ += entry + "\n"; }
        const std::string& log() const { return log_; }

    private:
        friend class base::RefCounted<Logger>;
        ~Logger() { }
        std::string log_;

        DISALLOW_COPY_AND_ASSIGN(Logger);
    };

    class LoggingFixedReceivedData final : public RequestPeer::ReceivedData {
    public:
        LoggingFixedReceivedData(const char* name,
            const char* s,
            scoped_refptr<Logger> logger)
            : name_(name)
            , data_(s, s + strlen(s))
            , logger_(logger)
        {
        }
        ~LoggingFixedReceivedData() override
        {
            logger_->Add(name_ + " is destructed.");
        }

        const char* payload() const override
        {
            return data_.empty() ? nullptr : &data_[0];
        }
        int length() const override { return static_cast<int>(data_.size()); }

    private:
        const std::string name_;
        const std::vector<char> data_;
        scoped_refptr<Logger> logger_;

        DISALLOW_COPY_AND_ASSIGN(LoggingFixedReceivedData);
    };

    class DestructionTrackingFunction
        : public base::RefCountedThreadSafe<DestructionTrackingFunction> {
    public:
        MOCK_METHOD0(Destruct, void(void));
        MOCK_METHOD0(Call, void(void));

    protected:
        friend class base::RefCountedThreadSafe<DestructionTrackingFunction>;
        virtual ~DestructionTrackingFunction() { Destruct(); }
    };

    class MockClient : public WebDataConsumerHandle::Client {
    public:
        MOCK_METHOD0(didGetReadable, void());
    };

    std::string ToString(const void* p, size_t size)
    {
        const char* q = static_cast<const char*>(p);
        return std::string(q, q + size);
    }

    class ThreadedSharedMemoryDataConsumerHandleTest : public ::testing::Test {
    protected:
        class ReadDataOperation;
        class ClientImpl final : public WebDataConsumerHandle::Client {
        public:
            explicit ClientImpl(ReadDataOperation* operation)
                : operation_(operation)
            {
            }

            void didGetReadable() override { operation_->ReadData(); }

        private:
            ReadDataOperation* operation_;
        };

        class ReadDataOperation final {
        public:
            typedef WebDataConsumerHandle::Result Result;
            ReadDataOperation(std::unique_ptr<SharedMemoryDataConsumerHandle> handle,
                base::MessageLoop* main_message_loop,
                const base::Closure& on_done)
                : handle_(std::move(handle))
                , main_message_loop_(main_message_loop)
                , on_done_(on_done)
            {
            }

            const std::string& result() const { return result_; }

            void ReadData()
            {
                if (!client_) {
                    client_.reset(new ClientImpl(this));
                    reader_ = handle_->obtainReader(client_.get());
                }

                Result rv = kOk;
                size_t read_size = 0;

                while (true) {
                    char buffer[16];
                    rv = reader_->read(&buffer, sizeof(buffer), kNone, &read_size);
                    if (rv != kOk)
                        break;
                    result_.insert(result_.size(), &buffer[0], read_size);
                }

                if (rv == kShouldWait) {
                    // Wait a while...
                    return;
                }

                if (rv != kDone) {
                    // Something is wrong.
                    result_ = "error";
                }

                // The operation is done.
                reader_.reset();
                main_message_loop_->task_runner()->PostTask(FROM_HERE, on_done_);
            }

        private:
            std::unique_ptr<SharedMemoryDataConsumerHandle> handle_;
            std::unique_ptr<WebDataConsumerHandle::Reader> reader_;
            std::unique_ptr<WebDataConsumerHandle::Client> client_;
            base::MessageLoop* main_message_loop_;
            base::Closure on_done_;
            std::string result_;
        };

        void SetUp() override
        {
            handle_.reset(
                new SharedMemoryDataConsumerHandle(kApplyBackpressure, &writer_));
        }

        StrictMock<MockClient> client_;
        std::unique_ptr<SharedMemoryDataConsumerHandle> handle_;
        std::unique_ptr<Writer> writer_;
        base::MessageLoop loop_;
    };

    class SharedMemoryDataConsumerHandleTest
        : public ::testing::TestWithParam<BackpressureMode> {
    protected:
        void SetUp() override
        {
            handle_.reset(new SharedMemoryDataConsumerHandle(GetParam(), &writer_));
        }
        std::unique_ptr<FixedReceivedData> NewFixedData(const char* s)
        {
            return base::MakeUnique<FixedReceivedData>(s, strlen(s));
        }

        StrictMock<MockClient> client_;
        std::unique_ptr<SharedMemoryDataConsumerHandle> handle_;
        std::unique_ptr<Writer> writer_;
        base::MessageLoop loop_;
    };

    void RunPostedTasks()
    {
        base::RunLoop run_loop;
        base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
            run_loop.QuitClosure());
        run_loop.Run();
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ReadFromEmpty)
    {
        char buffer[4];
        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(buffer, 4, kNone, &read);

        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, AutoClose)
    {
        char buffer[4];
        size_t read = 88;

        writer_.reset();
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(buffer, 4, kNone, &read);

        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ReadSimple)
    {
        writer_->AddData(NewFixedData("hello"));

        char buffer[4] = {};
        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(buffer, 3, kNone, &read);

        EXPECT_EQ(kOk, result);
        EXPECT_EQ(3u, read);
        EXPECT_STREQ("hel", buffer);

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, read);
        EXPECT_STREQ("lol", buffer);

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, read);

        writer_->Close();

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ReadAfterHandleIsGone)
    {
        writer_->AddData(NewFixedData("hello"));

        char buffer[8] = {};
        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);

        handle_.reset();

        Result result = reader->read(buffer, sizeof(buffer), kNone, &read);

        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, read);
        EXPECT_STREQ("hello", buffer);

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, read);

        writer_->Close();

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ReObtainReader)
    {
        writer_->AddData(NewFixedData("hello"));

        char buffer[4] = {};
        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(buffer, 3, kNone, &read);

        EXPECT_EQ(kOk, result);
        EXPECT_EQ(3u, read);
        EXPECT_STREQ("hel", buffer);

        reader.reset();
        reader = handle_->obtainReader(nullptr);

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, read);
        EXPECT_STREQ("lol", buffer);

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, read);

        writer_->Close();

        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CloseBeforeReading)
    {
        writer_->AddData(NewFixedData("hello"));
        writer_->Close();

        char buffer[20] = {};
        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(buffer, sizeof(buffer), kNone, &read);

        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, read);
        EXPECT_STREQ("hello", buffer);

        result = reader->read(buffer, sizeof(buffer), kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CloseWithDataBeforeZeroRead)
    {
        writer_->AddData(NewFixedData("hello"));
        writer_->Close();

        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(nullptr, 0, kNone, &read);

        EXPECT_EQ(kOk, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CloseWithoutDataBeforeZeroRead)
    {
        writer_->Close();

        size_t read = 88;
        auto reader = handle_->obtainReader(nullptr);
        Result result = reader->read(nullptr, 0, kNone, &read);

        EXPECT_EQ(kDone, result);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, AddMultipleData)
    {
        writer_->AddData(NewFixedData("Once "));
        writer_->AddData(NewFixedData("upon "));
        writer_->AddData(NewFixedData("a "));
        writer_->AddData(NewFixedData("time "));
        writer_->AddData(NewFixedData("there "));
        writer_->AddData(NewFixedData("was "));
        writer_->AddData(NewFixedData("a "));
        writer_->Close();

        char buffer[20];
        size_t read;
        Result result;

        auto reader = handle_->obtainReader(nullptr);
        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 6, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(6u, read);
        EXPECT_STREQ("Once u", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 2, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, read);
        EXPECT_STREQ("po", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 9, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(9u, read);
        EXPECT_STREQ("n a time ", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 3, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(3u, read);
        EXPECT_STREQ("the", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 20, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(9u, read);
        EXPECT_STREQ("re was a ", buffer);

        result = reader->read(buffer, sizeof(buffer), kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, AddMultipleDataInteractively)
    {
        writer_->AddData(NewFixedData("Once "));
        writer_->AddData(NewFixedData("upon "));

        char buffer[20];
        size_t read;
        Result result;

        auto reader = handle_->obtainReader(nullptr);
        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 6, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(6u, read);
        EXPECT_STREQ("Once u", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 2, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, read);
        EXPECT_STREQ("po", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 9, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, read);
        EXPECT_STREQ("n ", buffer);

        writer_->AddData(NewFixedData("a "));

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 1, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(1u, read);
        EXPECT_STREQ("a", buffer);

        writer_->AddData(NewFixedData("time "));
        writer_->AddData(NewFixedData("there "));
        writer_->AddData(NewFixedData("was "));
        writer_->AddData(NewFixedData("a "));
        writer_->Close();

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 9, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(9u, read);
        EXPECT_STREQ(" time the", buffer);

        std::fill(&buffer[0], &buffer[arraysize(buffer)], 0);
        result = reader->read(buffer, 20, kNone, &read);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(9u, read);
        EXPECT_STREQ("re was a ", buffer);

        result = reader->read(buffer, sizeof(buffer), kNone, &read);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, RegisterClient)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(4));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        RunPostedTasks();
        checkpoint.Call(2);
        writer_->Close();
        checkpoint.Call(3);
        RunPostedTasks();
        checkpoint.Call(4);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, RegisterClientWhenDataExists)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(3));

        checkpoint.Call(0);
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(1);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(2);
        RunPostedTasks();
        checkpoint.Call(3);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, AddDataWhenClientIsRegistered)
    {
        Checkpoint checkpoint;
        char buffer[20];
        Result result;
        size_t size;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(checkpoint, Call(4));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(5));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(2);
        writer_->AddData(NewFixedData("upon "));
        checkpoint.Call(3);
        result = reader->read(buffer, sizeof(buffer), kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(10u, size);
        checkpoint.Call(4);
        writer_->AddData(NewFixedData("a "));
        checkpoint.Call(5);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CloseWithClientAndData)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(2);
        writer_->Close();
        checkpoint.Call(3);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ReleaseReader)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(checkpoint, Call(2));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        reader.reset();
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(2);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, TwoPhaseReadShouldWait)
    {
        Result result;
        const void* buffer = &result;
        size_t size = 99;

        auto reader = handle_->obtainReader(nullptr);
        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(nullptr, buffer);
        EXPECT_EQ(0u, size);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, TwoPhaseReadSimple)
    {
        writer_->AddData(NewFixedData("Once "));

        Result result;
        const void* buffer = &result;
        size_t size = 99;

        auto reader = handle_->obtainReader(nullptr);
        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, size);
        EXPECT_EQ("Once ", ToString(buffer, 5));

        reader->endRead(1);

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(4u, size);
        EXPECT_EQ("nce ", ToString(buffer, 4));

        reader->endRead(4);

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, size);
        EXPECT_EQ(nullptr, buffer);

        writer_->Close();

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, size);
        EXPECT_EQ(nullptr, buffer);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CallOnClearWhenDestructed1)
    {
        // Call |on_clear| when the handle is gone and if there is no reader.
        Checkpoint checkpoint;
        scoped_refptr<DestructionTrackingFunction> on_clear(
            new StrictMock<DestructionTrackingFunction>);

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(*on_clear, Call());
        EXPECT_CALL(*on_clear, Destruct());
        EXPECT_CALL(checkpoint, Call(2));

        checkpoint.Call(0);
        handle_.reset(new SharedMemoryDataConsumerHandle(
            kApplyBackpressure,
            base::Bind(&DestructionTrackingFunction::Call, on_clear), &writer_));
        handle_.reset();
        on_clear = nullptr;
        checkpoint.Call(1);
        RunPostedTasks();
        checkpoint.Call(2);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, CallOnClearWhenDestructed2)
    {
        // Call |on_clear| when the reader is gone if the handle is alredy gone.
        Checkpoint checkpoint;
        scoped_refptr<DestructionTrackingFunction> on_clear(
            new StrictMock<DestructionTrackingFunction>);

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(*on_clear, Call());
        EXPECT_CALL(*on_clear, Destruct());
        EXPECT_CALL(checkpoint, Call(4));

        checkpoint.Call(0);
        handle_.reset(new SharedMemoryDataConsumerHandle(
            kApplyBackpressure,
            base::Bind(&DestructionTrackingFunction::Call, on_clear), &writer_));
        auto reader = handle_->obtainReader(nullptr);
        handle_.reset();
        on_clear = nullptr;
        checkpoint.Call(1);
        RunPostedTasks();
        checkpoint.Call(2);
        reader.reset();
        checkpoint.Call(3);
        RunPostedTasks();
        checkpoint.Call(4);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, DoNotCallOnClearWhenDone)
    {
        Checkpoint checkpoint;
        scoped_refptr<DestructionTrackingFunction> on_clear(
            new StrictMock<DestructionTrackingFunction>);

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(*on_clear, Destruct());
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(checkpoint, Call(4));

        checkpoint.Call(0);
        handle_.reset(new SharedMemoryDataConsumerHandle(
            kApplyBackpressure,
            base::Bind(&DestructionTrackingFunction::Call, on_clear), &writer_));
        on_clear = nullptr;
        checkpoint.Call(1);
        writer_->Close();
        checkpoint.Call(2);
        handle_.reset();
        checkpoint.Call(3);
        RunPostedTasks();
        checkpoint.Call(4);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, DoNotCallOnClearWhenErrored)
    {
        Checkpoint checkpoint;
        scoped_refptr<DestructionTrackingFunction> on_clear(
            new StrictMock<DestructionTrackingFunction>);

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(*on_clear, Destruct());
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(checkpoint, Call(4));

        checkpoint.Call(0);
        handle_.reset(new SharedMemoryDataConsumerHandle(
            kApplyBackpressure,
            base::Bind(&DestructionTrackingFunction::Call, on_clear), &writer_));
        on_clear = nullptr;
        checkpoint.Call(1);
        writer_->Fail();
        checkpoint.Call(2);
        handle_.reset();
        checkpoint.Call(3);
        RunPostedTasks();
        checkpoint.Call(4);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, TwoPhaseReadWithMultipleData)
    {
        writer_->AddData(NewFixedData("Once "));
        writer_->AddData(NewFixedData("upon "));

        Result result;
        const void* buffer = &result;
        size_t size = 99;

        auto reader = handle_->obtainReader(nullptr);
        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, size);
        EXPECT_EQ("Once ", ToString(buffer, 5));

        reader->endRead(1);

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(4u, size);
        EXPECT_EQ("nce ", ToString(buffer, 4));

        reader->endRead(4);

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, size);
        EXPECT_EQ("upon ", ToString(buffer, 5));

        reader->endRead(5);

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kShouldWait, result);
        EXPECT_EQ(0u, size);
        EXPECT_EQ(nullptr, buffer);

        writer_->Close();

        result = reader->beginRead(&buffer, kNone, &size);
        EXPECT_EQ(kDone, result);
        EXPECT_EQ(0u, size);
        EXPECT_EQ(nullptr, buffer);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ErrorRead)
    {
        Checkpoint checkpoint;
        Result result;
        char buffer[20] = {};
        size_t read = 99;
        auto reader = handle_->obtainReader(nullptr);

        writer_->Fail();
        result = reader->read(buffer, sizeof(buffer), kNone, &read);

        EXPECT_EQ(kUnexpectedError, result);
        EXPECT_EQ(0u, read);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, ErrorTwoPhaseRead)
    {
        Result result;
        const void* pointer = &result;
        size_t size = 99;
        auto reader = handle_->obtainReader(nullptr);

        writer_->Fail();
        result = reader->beginRead(&pointer, kNone, &size);

        EXPECT_EQ(kUnexpectedError, result);
        EXPECT_EQ(nullptr, pointer);
        EXPECT_EQ(0u, size);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, FailWhileTwoPhaseReadIsInProgress)
    {
        Result result;
        const void* pointer = nullptr;
        size_t size = 0;
        auto reader = handle_->obtainReader(nullptr);

        writer_->AddData(NewFixedData("Once "));
        result = reader->beginRead(&pointer, kNone, &size);
        auto* buffer = static_cast<const char*>(pointer);

        ASSERT_EQ(kOk, result);
        ASSERT_NE(nullptr, pointer);
        ASSERT_EQ(size, 5u);

        writer_->Fail();

        // We can access the buffer after calling |Fail|. I hope ASAN will detect
        // an error if the region is already freed.
        EXPECT_EQ('O', buffer[0]);
        EXPECT_EQ('n', buffer[1]);
        EXPECT_EQ('c', buffer[2]);
        EXPECT_EQ('e', buffer[3]);
        EXPECT_EQ(' ', buffer[4]);

        EXPECT_EQ(kOk, reader->endRead(size));

        EXPECT_EQ(kUnexpectedError, reader->beginRead(&pointer, kNone, &size));
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, FailWithClient)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(3));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        writer_->Fail();
        checkpoint.Call(2);
        RunPostedTasks();
        checkpoint.Call(3);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, FailWithClientAndData)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(checkpoint, Call(3));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(4));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(2);
        writer_->Fail();
        checkpoint.Call(3);
        RunPostedTasks();
        checkpoint.Call(4);
    }

    TEST_P(SharedMemoryDataConsumerHandleTest, RecursiveErrorNotification)
    {
        Checkpoint checkpoint;

        InSequence s;
        EXPECT_CALL(checkpoint, Call(0));
        EXPECT_CALL(checkpoint, Call(1));
        EXPECT_CALL(client_, didGetReadable())
            .WillOnce(Invoke(writer_.get(), &Writer::Fail));
        EXPECT_CALL(checkpoint, Call(2));
        EXPECT_CALL(client_, didGetReadable());
        EXPECT_CALL(checkpoint, Call(3));

        checkpoint.Call(0);
        auto reader = handle_->obtainReader(&client_);
        checkpoint.Call(1);
        writer_->AddData(NewFixedData("Once "));
        checkpoint.Call(2);
        RunPostedTasks();
        checkpoint.Call(3);
    }

    TEST(SharedMemoryDataConsumerHandleBackpressureTest, Read)
    {
        base::MessageLoop loop;
        char buffer[20];
        Result result;
        size_t size;

        std::unique_ptr<Writer> writer;
        auto handle = base::MakeUnique<SharedMemoryDataConsumerHandle>(
            kApplyBackpressure, &writer);
        scoped_refptr<Logger> logger(new Logger);
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data1", "Once ", logger));
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data2", "upon ", logger));
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data3", "a ", logger));
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data4", "time ", logger));

        auto reader = handle->obtainReader(nullptr);
        logger->Add("1");
        result = reader->read(buffer, 2, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, size);
        logger->Add("2");
        result = reader->read(buffer, 5, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(5u, size);
        logger->Add("3");
        result = reader->read(buffer, 6, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(6u, size);
        logger->Add("4");

        EXPECT_EQ(
            "1\n"
            "2\n"
            "data1 is destructed.\n"
            "3\n"
            "data2 is destructed.\n"
            "data3 is destructed.\n"
            "4\n",
            logger->log());
    }

    TEST(SharedMemoryDataConsumerHandleBackpressureTest, CloseAndReset)
    {
        base::MessageLoop loop;
        char buffer[20];
        Result result;
        size_t size;

        std::unique_ptr<Writer> writer;
        auto handle = base::MakeUnique<SharedMemoryDataConsumerHandle>(
            kApplyBackpressure, &writer);
        scoped_refptr<Logger> logger(new Logger);
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data1", "Once ", logger));
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data2", "upon ", logger));
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data3", "a ", logger));

        auto reader = handle->obtainReader(nullptr);
        logger->Add("1");
        result = reader->read(buffer, 2, kNone, &size);
        EXPECT_EQ(kOk, result);
        EXPECT_EQ(2u, size);
        logger->Add("2");
        writer->Close();
        logger->Add("3");
        handle.reset();
        reader.reset();
        logger->Add("4");

        std::vector<std::string> log = base::SplitString(
            logger->log(), "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);

        ASSERT_EQ(8u, log.size());
        EXPECT_EQ("1", log[0]);
        EXPECT_EQ("2", log[1]);
        EXPECT_EQ("3", log[2]);
        EXPECT_EQ("4", log[6]);
        EXPECT_EQ("", log[7]);

        // The destruction order doesn't matter in this case.
        std::vector<std::string> destruction_entries = { log[3], log[4], log[5] };
        std::sort(destruction_entries.begin(), destruction_entries.end());
        EXPECT_EQ(destruction_entries[0], "data1 is destructed.");
        EXPECT_EQ(destruction_entries[1], "data2 is destructed.");
        EXPECT_EQ(destruction_entries[2], "data3 is destructed.");
    }

    TEST(SharedMemoryDataConsumerHandleWithoutBackpressureTest, AddData)
    {
        base::MessageLoop loop;
        std::unique_ptr<Writer> writer;
        auto handle = base::MakeUnique<SharedMemoryDataConsumerHandle>(
            kDoNotApplyBackpressure, &writer);
        scoped_refptr<Logger> logger(new Logger);

        logger->Add("1");
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data1", "Once ", logger));
        logger->Add("2");
        writer->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data2", "upon ", logger));
        logger->Add("3");

        EXPECT_EQ(
            "1\n"
            "data1 is destructed.\n"
            "2\n"
            "data2 is destructed.\n"
            "3\n",
            logger->log());
    }

    TEST_F(ThreadedSharedMemoryDataConsumerHandleTest, Read)
    {
        base::RunLoop run_loop;
        auto operation = base::MakeUnique<ReadDataOperation>(
            std::move(handle_), &loop_, run_loop.QuitClosure());
        scoped_refptr<Logger> logger(new Logger);

        base::Thread t("DataConsumerHandle test thread");
        ASSERT_TRUE(t.Start());

        t.task_runner()->PostTask(FROM_HERE,
            base::Bind(&ReadDataOperation::ReadData,
                base::Unretained(operation.get())));

        logger->Add("1");
        writer_->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data1", "Once ", logger));
        writer_->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data2", "upon ", logger));
        writer_->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data3", "a time ", logger));
        writer_->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data4", "there ", logger));
        writer_->AddData(
            base::MakeUnique<LoggingFixedReceivedData>("data5", "was ", logger));
        writer_->Close();
        logger->Add("2");

        run_loop.Run();
        t.Stop();

        EXPECT_EQ("Once upon a time there was ", operation->result());
        EXPECT_EQ(
            "1\n"
            "2\n"
            "data1 is destructed.\n"
            "data2 is destructed.\n"
            "data3 is destructed.\n"
            "data4 is destructed.\n"
            "data5 is destructed.\n",
            logger->log());
    }

    INSTANTIATE_TEST_CASE_P(SharedMemoryDataConsumerHandleTest,
        SharedMemoryDataConsumerHandleTest,
        ::testing::Values(kApplyBackpressure,
            kDoNotApplyBackpressure));
} // namespace

} // namespace content
